package com.leadingsoft.pwxk.task;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CountDownLatch;

import javax.transaction.Transactional;

import org.springframework.context.annotation.Configuration;

import com.leadingsoft.pwxk.commons.DealDataResult;
import com.leadingsoft.pwxk.enums.InterfaceEnum;
import com.leadingsoft.pwxk.model.Summary;
import com.leadingsoft.pwxk.model.SyncRecord;
import com.leadingsoft.pwxk.services.AbstractService;
import com.leadingsoft.pwxk.services.SummaryService;
import com.leadingsoft.pwxk.services.SyncRecordService;
import com.leadingsoft.pwxk.services.impl.BaseInfoServiceImpl;
import com.leadingsoft.pwxk.services.impl.SummaryServiceImpl;
import com.leadingsoft.pwxk.services.impl.UndoBaseInfoServiceImpl;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

@Getter
@Setter
@Configuration
@Slf4j
public class FetchDataTask<T> implements Runnable {

    @SuppressWarnings("rawtypes")
    AbstractService abstractService;

    boolean needDid;

    InterfaceEnum interfaceEnum;

    private CountDownLatch countDownLatch;

    SyncRecordService syncRecordService;

    SyncRecord syncRecord = new SyncRecord();

    Object lock;

    SummaryService summaryService;

    boolean flag = true;

    public FetchDataTask() {

    }

    public FetchDataTask(@SuppressWarnings("rawtypes") AbstractService abstractService, InterfaceEnum interfaceEnum,
                         CountDownLatch countDownLatch, SyncRecordService syncRecordService, Object lock, boolean needDid) {
        this.abstractService = abstractService;
        this.interfaceEnum = interfaceEnum;
        this.needDid = needDid;
        this.countDownLatch = countDownLatch;
        this.syncRecordService = syncRecordService;
        this.lock = lock;
    }

    public FetchDataTask(@SuppressWarnings("rawtypes") AbstractService abstractService, InterfaceEnum interfaceEnum,
                         CountDownLatch countDownLatch, SyncRecordService syncRecordService, Object lock, SummaryService s) {
        this.abstractService = abstractService;
        this.interfaceEnum = interfaceEnum;
        this.needDid = true;
        this.countDownLatch = countDownLatch;
        this.syncRecordService = syncRecordService;
        this.lock = lock;
        this.summaryService = s;

    }

    @SuppressWarnings("unchecked")
    @Transactional(rollbackOn = Exception.class)
    void insertData() {
        try {
            log.info(this.interfaceEnum.getDesc() + "->进入数据获取任务");
            DealDataResult<T> dealDataResult = new DealDataResult<T>();
            syncRecord.setStartTime(new Date());
            syncRecord.setInterfaceName(interfaceEnum.getName());
            List<T> models = null;
            if (this.abstractService instanceof SummaryServiceImpl
                    || this.abstractService instanceof BaseInfoServiceImpl
                    || this.abstractService instanceof UndoBaseInfoServiceImpl) {
                models = addByNoDid(dealDataResult, interfaceEnum);
            } else {
                models = oneByNoDid(dealDataResult);
            }
            Long count = 0L;
            if (null != models && !models.isEmpty()) {
                Integer all = models.size();
                syncRecord.setExpectNumber(all);
                for (T model : models) {
                    try {
                        abstractService.save(model);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    count++;
                    // 本来一开始写finally里面，但是考虑万一强关，不会执行，所以每拉取一次数据就记录一次同步记录。
                    saveSyncRecord(all, count, this.abstractService.findLatestTime());
                }
            } else {
                noDataToSync();
            }
            log.info(this.interfaceEnum.getDesc() + "->退出数据获取任务");
        } catch (Exception e) {
            dealErrorInfo(e);
            log.error(this.interfaceEnum.getDesc() + "->获取数据任务异常:" + e.getMessage(), e);
        }
    }

    // 不需要dataid的增量更新
    private List<T> addByNoDid(DealDataResult<T> dealDataResult, InterfaceEnum interfaceName) {
        String syncTime = null;
        SyncRecord latestRecord = syncRecordService.findLatestRecord(interfaceName.getName());
        if (null != latestRecord) {
            syncTime = latestRecord.getSyncRecordTime();
        }
        if (null != syncTime) {
            syncTime = LocalDateTime.parse(syncTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")).plusSeconds(1)
                    .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        }
        return dealDataResult.listDataModel(null, this.interfaceEnum, syncTime);
    }

    // 行政区划，污染物信息，更新一次后不在更新数据。
    private List<T> oneByNoDid(DealDataResult<T> dealDataResult) {
        Long count = abstractService.getCount();
        if (null != count && count > 0) {
            return null;
        }
        return dealDataResult.listDataModel(null, this.interfaceEnum, null);
    }

    @SuppressWarnings("unchecked")
    @Transactional
    void insertInfo() {
        try {
            log.info(this.interfaceEnum.getDesc() + "->进入数据获取任务");
            DealDataResult<T> dealDataResult = new DealDataResult<T>();
            syncRecord.setStartTime(new Date());
            syncRecord.setInterfaceName(interfaceEnum.getName());
            List<Summary> summaries = fetchMessage(interfaceEnum);
            if (summaries == null) {
                noDataToSync();
                return;
            }
            Integer all = summaries.size();
            syncRecord.setExpectNumber(all);
            Long count = 0l;
            List<T> dataAll = new ArrayList<T>();
            // 遍历摘要信息，获取每个摘要对应的其它信息
            for (Summary summary : summaries) {
                List<T> models = dealDataResult.listDataModel(summary.getDataid(), this.interfaceEnum, null);
                if (null != models && !models.isEmpty()) {
                    dataAll.addAll(models);
                    try {
                        abstractService.saveAll(models);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                count++;
                //写在这里不合适吧  for循环里抛出异常 整个程序就会中断
                // 本来一开始写finally里面，但是考虑万一强关，不会执行，所以每拉取一次数据就记录一次同步记录。
                saveSyncRecord(all, count, summary.getItemendtime());
            }
            log.info(this.interfaceEnum.getDesc() + "->退出数据获取任务");
        } catch (Exception e) {
            // 记录异常信息
            dealErrorInfo(e);
            log.error(this.interfaceEnum.getDesc() + "->获取数据任务异常:" + e.getMessage(), e);
        }
    }

    private void noDataToSync() {
        syncRecord.setActualNumber(0);
        syncRecord.setExpectNumber(0);
        SyncRecord latestRecord = this.syncRecordService.findLatestRecord(interfaceEnum.getName());
        String syncTimes = null;
        if (null != latestRecord) {
            syncTimes = latestRecord.getSyncRecordTime();
        }
        syncRecord.setSyncRecordTime(syncTimes);
        syncRecord.setErrorNumber(0);
        syncRecord.setEndTime(new Date());
        syncRecordService.save(syncRecord);
    }

    // 保存同步数据记录
    private void saveSyncRecord(Integer all, Long count, String syncTime) {
        if (flag) {
            syncRecord.setActualNumber(count);
            syncRecord.setErrorNumber(all - count);
            syncRecord.setSyncRecordTime(syncTime);
            syncRecordService.save(syncRecord);
            flag = false;
        } else {
            SyncRecord latestRecord = syncRecordService.findLatestRecord(this.interfaceEnum.getName());
            latestRecord.setActualNumber(count);
            latestRecord.setErrorNumber(all - count);
            latestRecord.setEndTime(new Date());
            latestRecord.setSyncRecordTime(syncTime);
            syncRecordService.update(latestRecord);
        }
    }

    // 保存异常数据信息
    private void dealErrorInfo(Exception e) {
        if (flag) {
            if (e.getMessage().length() > 3000) {
                syncRecord.setErrorInfo(e.getMessage().substring(0, 3000));
            } else {
                syncRecord.setErrorInfo(e.getMessage());
            }
            syncRecord.setEndTime(new Date());
            syncRecordService.save(syncRecord);
        } else {
            SyncRecord latestRecord = syncRecordService.findLatestRecord(this.interfaceEnum.getName());
            if (e.getMessage().length() > 3000) {
                latestRecord.setErrorInfo(e.getMessage().substring(0, 3000));
            } else {
                latestRecord.setErrorInfo(e.getMessage());
            }
            latestRecord.setEndTime(new Date());
            syncRecordService.update(latestRecord);
        }
    }

    private List<Summary> fetchMessage(InterfaceEnum interfaceName) {
        // 获取所有的摘要信息
        String syncTime = null;
        SyncRecord latestRecord = syncRecordService.findLatestRecord(interfaceName.getName());
        if (null != latestRecord) {
            syncTime = latestRecord.getSyncRecordTime();
        }
        return summaryService.getSyncList(syncTime);
    }

    @Override
    public void run() {

        try {
            synchronized (lock) {
                long num = this.countDownLatch.getCount();
                if (!(this.abstractService instanceof SummaryServiceImpl) && num == 34) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        log.error("线程阻塞异常" + e.getMessage(), e);
                        return;
                    }
                }
            }
            // 这里面的方法以及处理异常了，所以此处直接try.....finally
            if (this.needDid) {
                insertInfo();
            } else {
                insertData();
            }
            if (this.abstractService instanceof SummaryServiceImpl) {
                synchronized (lock) {
                    lock.notifyAll();
                }
            }
        } finally {
            // 不管线程是否异常，计数器都要减一，否则获取最新数据将一直等待。countDown本身自带锁
            this.countDownLatch.countDown();
        }
    }

}
